import React from 'react';
import { findIndex, findLastIndex, isEmpty, floor, multiply, isArray, sortBy } from 'lodash';
import { Pagination, Button, Select, Row, Col } from 'antd';
import { ZoomInOutlined, ZoomOutOutlined, HighlightFilled } from '@ant-design/icons';
import Canvas from './Canvas';
import {
  DEFAULT_SCALE_VALUE,
  MIN_SCALE,
  MAX_SCALE,
  SCALES_ZOOM,
  calcAutoScale,
  calcRect,
} from './utils';

import styles from './index.less';

const Zoomer = ({ disabled, current = DEFAULT_SCALE_VALUE, zoomOut, zoomIn, zoomSelect }) => (
  <div className={styles.zoomer}>
    <Button
      shape="circle-outline"
      disabled={disabled || current <= MIN_SCALE}
      size="small"
      title="缩小"
      icon={<ZoomOutOutlined />}
      onClick={zoomOut}
    />
    <Button
      shape="circle"
      disabled={disabled || current >= MAX_SCALE}
      size="small"
      title="放大"
      icon={<ZoomInOutlined />}
      onClick={zoomIn}
      style={{ margin: '0 2px' }}
    />
    <Select
      size="small"
      disabled={disabled}
      defaultValue="auto"
      value={current}
      style={{ width: 150 }}
      onChange={zoomSelect}
    >
      <Select.Option value="auto">默认</Select.Option>
      <Select.Option value="page-actual">实际大小</Select.Option>
      <Select.Option value="page-fit">适应屏幕</Select.Option>
      <Select.Option value="page-width">适应宽度</Select.Option>
      <Select.Option value="0.5">50%</Select.Option>
      <Select.Option value="0.75">75%</Select.Option>
      <Select.Option value="1.0">100%</Select.Option>
      <Select.Option value="1.25">125%</Select.Option>
      <Select.Option value="1.5">150%</Select.Option>
      <Select.Option value="2.0">200%</Select.Option>
      <Select.Option value="3.0">300%</Select.Option>
      <Select.Option value="4.0">400%</Select.Option>
    </Select>
  </div>
);

const Action = ({ disabled, drawable = false, onDraw }) => (
  <div className={styles.action}>
    <Button
      size="small"
      disabled={disabled}
      title="添加标注"
      onClick={() => {
        onDraw(drawable);
      }}
    >
      标注 <HighlightFilled style={{ color: drawable ? 'red' : 'grey' }} />
    </Button>
  </div>
);

export default class Index extends React.PureComponent {

  constructor(props) {
    super(props);
    const { scale = 1, currentPage = 0 } = props;
    this.state = {
      loadingDoc: true,
      current: currentPage,
      totalPage: 0,
      scale,
      cWidth: 0, // 画布宽度
      cHeight: 0, // 画布宽度
      drawable: false,
      drawing: false,
      pdfObj: null,
      canvasRef: null,
      canvasContainerRef: null,
      startPoint: null,
      markData: [],
      scrollLeft: 0,
      scrollTop: 0,
    };

  }

  componentDidMount() {
    const { marks, page = 0 } = this.props;
    this.setState({
      markData: marks,
      current: page,
    })
    // console.log(this.getAbsoluteLocation(document.getElementById("canvasContainer")));
  }

  componentDidUpdate(prevProps) {
    const { marks, url, markable, page, scale } = this.props;
    if (prevProps.scale !== scale && scale !== this.state.scale) {
      this.pageZoom(scale);
    }
    if (prevProps.page !== page && page !== this.state.current) {
      const { totalPage } = this.state;
      let cur = 0;
      if (page < 1) {
        cur = 1;
      } else if (page > totalPage) {
        cur = totalPage;
      } else {
        cur = page;
      }
      this.state.current = cur;
      this.initPage(cur);
    }
    if (prevProps.markable !== markable) {
      this.state.drawable = false;
      this.state.drawing = false;
    }
    if (prevProps.marks !== marks) {
      if (isArray(marks)) {
        this.state.markData = marks;
        if (url === prevProps.url) {
          if (url !== null && url !== '' && url !== undefined) {
            const { current } = this.state;
            let currentPage = current;
            if (marks.length > 0) {
              const sortedMarks = sortBy(marks, ['page']);
              const { page: minPage } = sortedMarks[0];
              if (currentPage !== minPage) {
                currentPage = minPage;
              }
            }
            if (currentPage === current) {
              const { canvasRef } = this.state;
              if (canvasRef) {
                const context = canvasRef.getContext('2d');
                if (context) {
                  const { cWidth, cHeight, imageData, } = this.state;
                  if (imageData) {
                    context.clearRect(0, 0, cWidth, cHeight);
                    context.putImageData(imageData, 0, 0);
                  }
                  this.drawHistoryGraph(context, marks.filter(m => m.page === current));
                }
              }
            } else {
              this.initPage(currentPage);
            }
          } else {
            this.initState();
          }
        }
      }
    }
  }

  setRef = (ref) => {
    this.setState({
      canvasContainerRef: ref,
    });
    const { setRef } = this.props;
    if (setRef) {
      setRef(ref);
    }
  }

  getAbsoluteLocation = (element) => {
    if (element == null) {
      return null;
    }
    let { offsetTop, offsetLeft } = element;
    const { offsetWidth, offsetHeight } = element;
    while (element === element.offsetParent) {
      offsetTop += element.offsetTop;
      offsetLeft += element.offsetLeft;
    }
    return {
      absoluteTop: offsetTop,
      absoluteLeft: offsetLeft,
      offsetWidth,
      offsetHeight,
    };
  }

  initState = () => {
    const { scale, currentPage = 0 } = this.props;
    this.setState({
      loadingDoc: true,
      current: currentPage,
      totalPage: 0,
      scale,
      cWidth: 0, // 画布宽度
      cHeight: 0, // 画布宽度
      drawable: false,
      drawing: false,
      draging: false,
      pdfObj: null,
      startPoint: null,
      markData: [],
      imageData: null,
    });
  }

  handleNoData = (canvasRef) => {
    // eslint-disable-next-line no-param-reassign
    canvasRef.width = 0;
    // eslint-disable-next-line no-param-reassign
    canvasRef.height = 0;
    this.initState();
  }

  handlePdfLoadingSuccess = (pdf, canvasRef) => {
    const { numPages } = pdf;
    this.setState(
      {
        totalPage: numPages,
        pdfObj: pdf,
        canvasRef,
        markData: [],
        drawable: false,
        drawing: false,
        draging: false,
      },
      () => {
        if (numPages > 0) {
          this.initPage(1);
          // this.initGraphList(numPages);
        }
      },
    );
    const { onPdfLoadingSuccess } = this.props;
    if (onPdfLoadingSuccess) {
      onPdfLoadingSuccess();
    }
  };

  handlePageChange = (current) => {
    const { onPageChange } = this.props;
    if (onPageChange) {
      onPageChange(current);
    }
  }

  initPage = (current) => {
    const t = this;
    const { totalPage } = t.state;
    let cur = 0;
    if (current === 0) {
      cur = 1;
    } else if (current > totalPage) {
      cur = totalPage;
    } else {
      cur = current;
    }
    this.setState({
      loadingDoc: true,
      drawable: false,
      drawing: false,
      current: cur,
    }, () => {
      const { pdfObj, canvasRef, markData } = t.state;
      if (current > totalPage || current === 0) {
        return;
      }
      pdfObj.getPage(current).then((page) => {
        const viewPort = page.getViewport({ scale: 1 });
        const autoScale = calcAutoScale(
          viewPort.width,
          window.innerWidth,
          viewPort.height,
          window.innerHeight,
        );
        const viewport = page.getViewport({ scale: autoScale });
        // 创建 context 对象 (内建的 HTML5 对象: 拥有多种绘制路径、矩形、圆形、字符以及添加图像的方法)
        const context = canvasRef.getContext('2d');
        canvasRef.height = viewport.height;
        canvasRef.width = viewport.width;
        // const imageData = context.getImageData(0, 0, canvasRef.width, canvasRef.height);
        page
          .render({
            canvasContext: context,
            viewport,
          })
          .promise.then(() => {
            t.setState(
              {
                // current,
                scale: autoScale,
                imageData: context.getImageData(0, 0, canvasRef.width, canvasRef.height),
                cWidth: canvasRef.width,
                cHeight: canvasRef.height,
                loadingDoc: false,
              },
              () => {
                const graphList = markData.filter(m => m.page === current);
                this.drawHistoryGraph(context, graphList);
              },
            );
          });
      });
    });
  };

  pageZoom = (scale) => {
    const t = this;
    t.setState({
      loadingDoc: true,
    }, () => {
      const { current, pdfObj, canvasRef, markData } = t.state;
      pdfObj.getPage(current).then((page) => {
        const viewport = page.getViewport({ scale });
        // 创建 context 对象 (内建的 HTML5 对象: 拥有多种绘制路径、矩形、圆形、字符以及添加图像的方法)
        const context = canvasRef.getContext('2d');
        canvasRef.height = viewport.height;
        canvasRef.width = viewport.width;
        page
          .render({
            canvasContext: context,
            viewport,
          })
          .promise.then(() => {
            t.setState(
              {
                scale,
                imageData: context.getImageData(0, 0, canvasRef.width, canvasRef.height),
                cWidth: canvasRef.width,
                cHeight: canvasRef.height,
                loadingDoc: false,
              },
              () => {
                const graphList = markData.filter(m => m.page === current);
                this.drawHistoryGraph(context, graphList);
              },
            );
          });
      });
    });
  };

  handleStatusChange = (payload) => {
    this.setState(payload);
  };

  /**
   * 缩小
   */
  handleZoomOut = (e) => {
    e.preventDefault();
    const { scale } = this.state;
    const index = findIndex(SCALES_ZOOM, (s) => s >= scale);
    if (index > 0) {
      const newScales = SCALES_ZOOM[index - 1];
      this.setState({
        scale: newScales,
      }, () => {
        this.pageZoom(newScales);
        const { onZoom } = this.props;
        if (onZoom) {
          onZoom(newScales);
        }
      });
    }
  };

  /**
   * 放大
   */
  handleZoomIn = (e) => {
    e.preventDefault();
    const { scale } = this.state;
    const index = findLastIndex(SCALES_ZOOM, (s) => s <= scale);
    if (index < SCALES_ZOOM.length) {
      const newScales = SCALES_ZOOM[index + 1];
      this.setState({
        scale: newScales,
      }, () => {
        this.pageZoom(newScales);
        const { onZoom } = this.props;
        if (onZoom) {
          onZoom(newScales);
        }
      });
    }
  };

  handleZoomSelect = () => { };

  handleDraw = (drawable) => {
    this.handleStatusChange({ drawable: !drawable, draging: false });
  };

  handleUpload = (file) => {
    this.handleStatusChange({ file });
  };

  transPoint = (val) => {
    const { scale } = this.state;
    return floor(multiply(val, scale));
  };

  saveGraph = (point) => {
    const { singleDraw = true } = this.props;
    if (!singleDraw) {
      const { current, markData } = this.state;
      const marks = markData.push({
        page: current,
        type: 'rect',
        data: point,
      });
      this.setState({
        markData: marks,
      });
    }
  };

  drawHistoryGraph = (ctx, graphHistory) => {
    if (!isEmpty(graphHistory)) {
      graphHistory.forEach((graph) => {
        const { type, data } = graph;
        if (type === 'rect') {
          this.drawRect(ctx, data);
        }
      });
    }
  };

  drawRect = (ctx, point) => {
    ctx.strokeStyle = 'red';
    ctx.lineWidth = 2;
    ctx.strokeRect(
      this.transPoint(point.x),
      this.transPoint(point.y),
      this.transPoint(point.width),
      this.transPoint(point.height),
    );
  };

  handleMouseDown = (e) => {
    e.preventDefault();
    const { drawable, canvasRef } = this.state;
    const point = this.makePoint(e, canvasRef);
    if (drawable) {
      this.setState({
        startPoint: point,
        drawing: true,
        draging: false,
      });
    } else {
      const { canvasContainerRef } = this.state;
      let scrollLeft = 0;
      let scrollTop = 0;
      if (canvasContainerRef) {
        scrollLeft = canvasContainerRef.scrollLeft;
        scrollTop = canvasContainerRef.scrollTop;
      }
      this.setState({
        scrollLeft,
        scrollTop,
        startPoint: point,
        draging: true,
      });
    }
  };

  handleMouseUp = (e) => {
    e.preventDefault();
    const { scale, drawable, startPoint, canvasRef } = this.state;
    if (drawable) {
      const point = this.makePoint(e, canvasRef);
      if (startPoint && point) {
        const rect = calcRect(startPoint.x, startPoint.y, point.x, point.y, scale);
        this.saveGraph(rect);
        this.setState({ startPoint: null, drawing: false });
        const { onDrawComplete } = this.props;
        if (onDrawComplete) {
          const { current } = this.state;
          onDrawComplete({ page: current, type: 'rect', data: rect });
        }
      }
    } else {
      this.setState({
        draging: false,
      });
    }
  };

  handleMouseMove = (e) => {
    e.preventDefault();
    const { scale, drawable, drawing, draging, canvasRef, startPoint, imageData } = this.state;
    const point = this.makePoint(e, canvasRef);
    if (drawable && drawing) {
      if (startPoint && point) {
        const rect = calcRect(startPoint.x, startPoint.y, point.x, point.y, scale);
        const context = canvasRef.getContext('2d');
        if (context) {
          const { singleDraw = true } = this.props;
          const { current, markData, cWidth, cHeight } = this.state;
          context.clearRect(0, 0, cWidth, cHeight);
          context.putImageData(imageData, 0, 0);
          if (!singleDraw) {
            this.drawHistoryGraph(context, markData.filter(m => m.page === current));
          }
          this.drawRect(context, rect);
        }
      }
    } else if (draging) {
      const { onScroll } = this.props;
      const { canvasContainerRef, scrollLeft, scrollTop } = this.state;
      const mx = point.x - startPoint.x;
      const my = point.y - startPoint.y;
      if (onScroll) {
        onScroll(scrollLeft - mx, scrollTop - my);
      } else if (canvasContainerRef) {
        canvasContainerRef.scrollLeft = scrollLeft - mx;
        canvasContainerRef.scrollTop = scrollTop - my;
      }
    }
  };

  handleMouseOut = () => {
    this.setState({
      draging: false,
    });
  }

  handleMouseOver = () => { }

  makePoint = (e, canvasRef) => {
    if (canvasRef) {
      const { clientX, clientY } = e;
      const { top, left } = canvasRef.getBoundingClientRect();

      return {
        x: clientX - left,
        y: clientY - top,
      };
    }
    return null;
  };

  render() {
    const { url, redrawData, markable = false, canvasDivHeight } = this.props;
    const { current, totalPage, drawable, loadingDoc, drawing } = this.state;
    return (
      <div id="canvasContainer" style={{ minWidth: 320 }}>
        <div className={styles.toolbar}>
          <Row align="middle">
            <Col flex="0 1 200px">
              <Pagination
                disabled={loadingDoc || drawing}
                simple
                className={styles.pagination}
                total={totalPage}
                current={current}
                pageSize={1}
                showQuickJumper
                showSizeChanger={false}
                onChange={(cur) => {
                  this.setState({
                    loadingDoc: true,
                    drawable: false,
                    drawing: false,
                  }, () => {
                    this.handlePageChange(cur);
                    this.initPage(cur);
                  })
                }}
              />
            </Col>
            <Col flex="auto">
              <Zoomer
                disabled={loadingDoc || drawing}
                zoomOut={this.handleZoomOut}
                zoomIn={this.handleZoomIn}
                zoomSelect={this.handleZoomSelect}
              />
            </Col>
            <Col flex="0 1 200px">
              <Action
                markable={markable}
                disabled={loadingDoc || drawing || !markable}
                drawable={drawable && markable}
                onDraw={this.handleDraw}
                onUpload={this.handleUpload}
              />
            </Col>
          </Row>
        </div>
        <Canvas
          url={url}
          setRef={this.setRef}
          height={canvasDivHeight}
          redrawData={redrawData}
          markable={markable}
          {...this.state}
          onNodata={this.handleNoData}
          onPdfLoading={() => {
            const { onPdfLoading } = this.props;
            if (onPdfLoading) {
              onPdfLoading();
            }
          }}
          onPdfLoadingSuccess={this.handlePdfLoadingSuccess}
          changeStatus={this.handleStatusChange}
          onMouseDown={this.handleMouseDown}
          onMouseUp={this.handleMouseUp}
          onMouseMove={this.handleMouseMove}
          onMouseOut={this.handleMouseOut}
          onMouseOver={this.handleMouseOver}
        />
      </div>
    );
  }
}
